package com.foreveross.crawl.common.jpa;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.foreveross.crawl.common.db.hbase.HBaseCellModel;
import com.foreveross.crawl.common.db.hbase.HBaseRowModel;
import com.foreveross.crawl.common.db.hbase.HbaseManager;
import com.foreveross.crawl.common.domain.BaseHBaseEntity;
import com.foreveross.crawl.common.domain.IBaseEntity;
import com.foreveross.crawl.common.jpa.annontation.HBaseColumn;
import com.foreveross.crawl.common.util.ClassUtils;
import com.foreveross.crawl.common.util.convert.ValueConvertFatory;
/**
 * hbase的JPA实现
 * @author xiangsf 2013-2-16
 *
 * @param <T>
 * @param <PK>
 */
@Repository("entityHBaseRepository")
public class EntityHBaseRepository implements IBaseRepository{
	/**
	 * log4j日志记录
	 */
	protected static Logger logger = Logger.getLogger(EntityHBaseRepository.class);
	@Autowired
	private HbaseManager hbaseManager;

	protected <T extends IBaseEntity> String getHBaseTableName(Class<T> clazz){
		javax.persistence.Table ta = clazz.getAnnotation(javax.persistence.Table.class);
		return ta.name();
	}

	/**
	 * 将HBaseRowModel对象通过Entity定义转换为IBaseEntity
	 * @param model
	 * @return IBaseEntity
	 */
	protected <T extends IBaseEntity> T convert2Entity(Class<T> clazz, HBaseRowModel model){
		
		if(model == null) return null;
		T entity = null;
		HBaseCellModel cell = null;
		HBaseColumn hca = null;
		OneToMany otm = null;
		List<Field> fields = null;
		try {
			entity = clazz.newInstance();
			fields = ClassUtils.getBeanAllFields(clazz);
			
			for(Field f : fields){
				//凡是集合对象的，暂不保存
				if(f.getAnnotation(ManyToOne.class) != null ||
						f.getAnnotation(OneToOne.class) != null) continue;
				otm = f.getAnnotation(OneToMany.class);
				if(otm != null){
					//TODO
				}
				hca = f.getAnnotation(HBaseColumn.class);
				if(hca != null){
					//使用columnDefinition与name作为列簇，列名
					cell = model.lookupCell(hca.family(), hca.qualifier());
					if(cell != null){
						f.set(entity, ValueConvertFatory.string2Object(f.getGenericType().toString(), cell.getValue(), hca.format()));
					}
				}
			}
			
			entity.setId(model.getRowkey());
		}catch (Exception e) {
			e.printStackTrace();
			throw new HBaseJpaException(e);
		}finally{
			 cell = null;
			 hca = null;
			 fields = null;
		}
		return entity;
	}
	/**
	 * 将HBaseRowModel对象通过Entity定义转换为IBaseEntity
	 * @param model
	 * @return IBaseEntity
	 */
	protected <T extends IBaseEntity> HBaseRowModel convert2HBaseModel(T entity){
		
		if(entity == null) return null;
		
		HBaseRowModel model = null;

		try {
			//这里先生成rowkey值，以供后面调用，以及entity回调获取rowkey
			entity.setId(((BaseHBaseEntity)entity).generateRowKey());
			model = new HBaseRowModel();
			//设置rowkey值
			model.setRowkey((String)entity.getId());
			
			this.convert2CellModel(model, entity);
			
			
			return model;
		}catch (Exception e) {
			e.printStackTrace();
			throw new HBaseJpaException(e);
		}finally{

		}
	}
	/**
	 * 将集合映射转换为hbase单元cell的列值集合
	 * <p>映射上是一对多的关系时，将集合中对象按@HBaseColumn配置，组装成列值集合,
	 * 若集合包含对象的数量多于一个时，按自然顺序将列名加上后缀_0,_1,_2...... 从0开始
	 * @param list
	 * @return
	 * @throws Exception 
	 */
	protected <T extends IBaseEntity> void convert2CellModels(HBaseRowModel model,Collection<T> list, int layer) throws Exception{
		if(list != null){
			int i = 0;
			for(T e : list){
				this.convert2CellModel(model, e, layer, i++);
			}
		}
	}
	/**
	 * 将一个实体对象转换成HBaseCellModel
	 * <p>若是集合时，将会传入一个非负数的列序号，用于组装存储的实际列名
	 * @param <T>
	 * @param model
	 * @param entity
	 * @param i
	 * @throws Exception
	 */
	protected <T extends IBaseEntity> void convert2CellModel(HBaseRowModel model, T entity, int layer, int i) throws Exception{
		HBaseCellModel cell = null;
		PropertyDescriptor pd = null;
		HBaseColumn hca = null;
		Object obj = null;
		List<Field> fields = null;
		String value = null;
		try{
			fields = ClassUtils.getBeanAllFields(entity.getClass());
			for(Field f : fields){
				//凡是集合对象的，暂不保存
				if(f.getAnnotation(ManyToOne.class) != null ||
						f.getAnnotation(OneToOne.class) != null) continue;
				
				hca = f.getAnnotation(HBaseColumn.class);
				if(hca != null){
					pd = new PropertyDescriptor(f.getName(), entity.getClass());
					obj = pd.getReadMethod().invoke(entity, null);
					if(obj instanceof Collection && 
							f.getAnnotation(OneToMany.class) != null){
						this.convert2CellModels(model, (Collection)obj, i);
					}else{
						//使用columnDefinition与name作为列簇，列名
						value = ValueConvertFatory.object2String(f.getGenericType().toString(), obj, hca.format());
						if(i > -1){
							String qualifier = layer > -1 ? hca.qualifier()+"_"+layer+"_"+i : hca.qualifier()+"_"+i;
							cell = new HBaseCellModel(hca.family(), qualifier, 0L, value);//列名=列名+_数字
						}else{
							cell = new HBaseCellModel(hca.family(), hca.qualifier(), 0L, value);
						}
						cell.setRowModel(model);
						model.addCell(cell);
					}
				}
			}	
		}finally{
			 cell = null;
			 hca = null;
			 fields = null;
			 pd = null;
			 obj = null;
			 value = null;
		}
	}
	protected <T extends IBaseEntity> void convert2CellModel(HBaseRowModel model, T entity) throws Exception{
		this.convert2CellModel(model, entity,-1, -1);
	}
	@Override
	public <T extends IBaseEntity> T get(Class<T> clazz, Serializable id) {
		HBaseRowModel model = null;
		try {
			 model = hbaseManager.getOneRecord(getHBaseTableName(clazz), (String)id);
			 return convert2Entity(clazz, model);
		} catch (Exception e) {
			e.printStackTrace();
			throw new HBaseJpaException(e);
		}finally{
			model = null;
		}
	}

	@Override
	public <T extends IBaseEntity> boolean exists(Class<T> clazz, Serializable id) {
		return get(clazz, id) != null;
	}
	/**
	 * 一般不使用， 数据一般会很多导致内存溢出
	 */
	@Deprecated 
	@Override
	public <T extends IBaseEntity> List<T> findAll(Class<T> clazz) {
		List<HBaseRowModel> list = null;
		List<T> results = null;
		try {
			results = new ArrayList<T>();
			list = hbaseManager.getMultRecord(getHBaseTableName(clazz), null);
			for(HBaseRowModel m : list){
				results.add(this.convert2Entity(clazz, m));
			}
			return results;
		} catch (Exception e) {
			e.printStackTrace();
			throw new HBaseJpaException(e);
		}finally{
			list = null;
		}
	}

	@Override
	public <T extends IBaseEntity> void create(T o) {
		HBaseRowModel model = null;
		try {
			model = this.convert2HBaseModel(o);
			hbaseManager.addRecord(getHBaseTableName(o.getClass()), model);
		} catch (Exception e) {
			logger.error(e);
			throw new HBaseJpaException(e);
		}finally{
			model = null;
		}
	}
	/**
	 * 更新也是创建
	 */
	@Override
	public <T extends IBaseEntity> void update(T o) {
		this.create(o);
	}

	@Override
	public <T extends IBaseEntity> void delete(Class<T> clazz, Serializable id) {
		try {
			hbaseManager.delOneRecord(getHBaseTableName(clazz), (String)id);
		} catch (Exception e) {
			e.printStackTrace();
			throw new HBaseJpaException(e);
		}
	}

	@Override
	public <T extends IBaseEntity> void delete(T o) {
		this.delete(o.getClass(), o.getId());
	}
	/**
	 * 保存也是创建
	 */
	@Override
	public <T extends IBaseEntity> void save(T o) {
		this.create(o);
	}

	@Override
	public <T extends IBaseEntity> T find(Class<T> clazz, Serializable id) {
		return this.get(clazz, id);
	}

	@Override
	public <T extends IBaseEntity> void create(List<T> instances) {
		for(T t : instances){
			this.create(t);
		}
	}
	@Override
	public <T extends IBaseEntity> void update(List<T> instances) {
		for(T t : instances){
			this.update(t);
		}
	}
	@Override
	public <T extends IBaseEntity> void delete(List<T> instances) {
		for(T t : instances){
			this.delete(t);
		}
	}
	@Override
	public <T extends IBaseEntity> void save(List<T> instances) {
		for(T t : instances){
			this.save(t);
		}
	}

}
